--------------Trap-A-Zoid--------------
A 4am crack                  2017-05-02
---------------------------------------

Name: Trap-A-Zoid
Genre: educational
Year: 1983
Publisher: DesignWare
Platform: Apple ][+ or later
Media: single-sided 5.25-inch floppy
OS: custom
Previous cracks: none
Similar cracks:
  #894 Number Stumper
  #840 Jumpman
  #595 Addition Magician
  #476 Microzine 2

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  immediate disk read error

Locksmith Fast Disk Backup
  unable to read any track

EDD 4 bit copy (no sync, no count)
  copy displays a graphical title page
    then hangs with the drive motor on

Copy ][+ nibble editor
  T00 -> standard prologues, modified
    epilogues (FF FF FF)
  T01,02 -> not full tracks? looks
    like they have some standard-ish
    sectors, but not 16 per track
  T03-07 -> corrupted address fields
    that claim to be track $00
  T08-T22 -> uncorrupted address fields
    but still non-standard epilogues

When I say "corrupted address fields,"
this is what that looks like (example
from track $07):

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 07  START: 2F57  LENGTH: 18A0

2F30: FF FF FF FF FF FF FF FF   VIEW
2F38: FF FF FF FF FF FF FF FF
2F40: FF FF FF FF FF FF FF FF
2F48: FF FF FF FF FF FF FF FF
2F50: FF FF FF FF FF D5 AA 96 <-2F55
                     ^^^^^^^^
                 address prologue

2F58: AA AA AA AA AA AA AA AA
      ^^^^^ ^^^^^ ^^^^^ ^^^^^
      V000   T00   S00  chksm

2F60: FF FF FF 97 FC FF FF FF
      ^^^^^^^^
  address epilogue

2F68: FF D5 AA AD D7 AB D7 AC
         ^^^^^^^^
      data prologue

2F70: ED B6 EA E6 F3 BF 96 DF

---------------------------------------

  A  TO ANALYZE DATA  ESC TO QUIT

  ?  FOR HELP SCREEN  /  CHANGE PARMS

  Q  FOR NEXT TRACK   SPACE TO RE-READ

                 --^--

The disk is lying to me. The address
field claims to be track $00, but it's
really track $07. Bad disk! Stop lying!

Disk Fixer
  ["O" -> "Input/Output Control"]
    set Address Epilogue to "FF FF FF"
    set Data Epilogue to "FF FF FF"
  T00 readable, disk volume is 000 (!)
  T01-T07 unreadable (no option to
    ignore the corrupted address field)
  T08-T22 readable, disk volume is 000

Copy ][+ sector editor
  ["P" -> "Sector Editor Patcher"]
    set type to "CUSTOM"
    set Address Epilogue to "FF FF"
    set Data Epilogue to "FF FF FF"
  T00 readable
  T08-T22 readable

  ["P" -> "Sector Editor Patcher"]
    set CHECK TRACK to "NO"
  only parts of T01 and T02 readable:
    T01: S03,04,05,06,07,0A,0B,0C,0D,0E
    T02: S01,02,08,09,0F
  T03-07 readable!

Why didn't COPYA work?
  modified epilogue bytes on track $00
  (it never even got to the fun part)

Why didn't Locksmith FDB work?
  ditto

Why didn't my EDD copy work?
  I've seen similar disks, where the
  first N tracks have intentionally
  corrupted address fields. (N varies
  from disk to disk.) There's a custom
  loader that loads the data from those
  corrupted tracks, then transfers
  control to a standard RWTS for the
  rest of the program. Somewhere in the
  corrupted tracks, it will load data
  from consecutive half tracks. (These
  are devilishly difficult to copy, and
  I didn't even try.) That's just an
  educated guess; I could be surprised.

Hey, I can actually validate that guess
in the Copy ][+ nibble editor, which
can read half tracks.

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 01.50  START: 38CA  LENGTH: 17CD

3B20: FF FF FF FF FF FF FF FF   VIEW
3B28: FF FF FF FF FF FF FF FF
3B30: FF E4 FF FF 9F E7 F9 FE
3B38: FF FF FF FF FF D5 AA 96 <-3B35
                     ^^^^^^^^
                  address prologue

3B40: AA AA AA AA AF AB AF AB
      ^^^^^ ^^^^^ ^^^^^ ^^^^^
      V000   T00   S0B  chksm

3B48: FF FF FF DF F3 FC FF FF
      ^^^^^^^^
  address eplogue

3B50: FF D5 AA AD D9 DB EB D9
         ^^^^^^^^
      data prologue

3B58: BB FC ED FA B3 D6 DB DA
3B60: F2 FA DA F2 EE D6 96 AD

---------------------------------------

  A  TO ANALYZE DATA  ESC TO QUIT

  ?  FOR HELP SCREEN  /  CHANGE PARMS

  Q  FOR NEXT TRACK   SPACE TO RE-READ

                 --^--

Jackpot! (Note that it's still claiming
to be track $00, though, just like the
other whole tracks above and below it.)

Next steps:

  1. Trace the boot
  2. ???

                   ~

               Chapter 1
      In Which We Brag About Our
           Humble Beginnings


I have two floppy drives, one in slot 6
and the other in slot 5. My "work disk"
(in slot 5) runs Diversi-DOS 64K, which
is compatible with Apple DOS 3.3 but
relocates most of DOS to the language
card on boot. This frees up most of
main memory (only using a single page
at $BF00..$BFFF), which is useful for
loading large files or examining code
that lives in areas typically reserved
for DOS.

[S6,D1=original disk]
[S5,D1=my work disk]

The floppy drive firmware code at $C600
is responsible for aligning the drive
head and reading sector 0 of track 0
into main memory at $0800. Because the
drive can be connected to any slot, the
firmware code can't assume it's loaded
at $C600. If the floppy drive card were
removed from slot 6 and reinstalled in
slot 5, the firmware code would load at
$C500 instead.

To accommodate this, the firmware does
some fancy stack manipulation to detect
where it is in memory (which is a neat
trick, since the 6502 program counter
is not generally accessible). However,
due to space constraints, the detection
code only cares about the lower 4 bits
of the high byte of its own address.

Stay with me, this is all about to come
together and go boom.

$C600 (or $C500, or anywhere in $Cx00)
is read-only memory. I can't change it,
which means I can't stop it from
transferring control to the boot sector
of the disk once it's in memory. BUT!
The disk firmware code works unmodified
at any address. Any address that ends
with $x600 will boot slot 6, including
$B600, $A600, $9600, &c.

; copy drive firmware to $9600
*9600<C600.C6FFM

; and execute it
*9600G
...reboots slot 6, loads game...

Now then:

]PR#5
...
]CALL -151

*9600<C600.C6FFM

*96F8L

96F8-   4C 01 08    JMP   $0801

That's where the disk controller ROM
code ends and the on-disk code begins.
But $9600 is part of read/write memory.
I can change it at will. So I can
interrupt the boot process after the
drive firmware loads the boot sector
from the disk but before it transfers
control to the disk's bootloader.

; instead of jumping to on-disk code,
; copy boot sector to higher memory so
; it survives a reboot
96F8-   A0 00       LDY   #$00
96FA-   B9 00 08    LDA   $0800,Y
96FD-   99 00 28    STA   $2800,Y
9700-   C8          INY
9701-   D0 F7       BNE   $96FA

; turn off slot 6 drive motor
9703-   AD E8 C0    LDA   $C0E8

; reboot to my work disk in slot 5
9706-   4C 00 C5    JMP   $C500

*BSAVE TRACE,A$9600,L$109
*9600G
...reboots slot 6...
...reboots slot 5...

]BSAVE OBJ.0800-08FF,A$2800,L$100

Now we get to(*) trace the boot process
one sector, one page, one instruction
at a time.

(*) If you replace the words "need to"
    with the words "get to," life
    becomes amazing.

                   ~

               Chapter 2
         Boot Trace and Chill


]CALL -151

; move boot0 back into place
*800<2800.28FFM

*801L

; set reset vector
0801-   8A          TXA
0802-   4A          LSR
0803-   4A          LSR
0804-   4A          LSR
0805-   4A          LSR
0806-   09 C0       ORA   #$C0
0808-   85 3F       STA   $3F
080A-   8D F3 03    STA   $03F3
080D-   49 A5       EOR   #$A5
080F-   8D F4 03    STA   $03F4
0812-   A9 00       LDA   #$00
0814-   8D F2 03    STA   $03F2

; hmm
0817-   A9 04       LDA   #$04
0819-   48          PHA

; machine initialization (memory banks,
; TEXT, IN#0, PR#0, &c.)
081A-   8D 81 C0    STA   $C081
081D-   20 2F FB    JSR   $FB2F
0820-   8D 52 C0    STA   $C052
0823-   20 89 FE    JSR   $FE89
0826-   20 93 FE    JSR   $FE93

; clear hi-res screen 1
0829-   A2 20       LDX   #$20
082B-   A0 00       LDY   #$00
082D-   84 06       STY   $06
082F-   A9 20       LDA   #$20
0831-   85 07       STA   $07
0833-   98          TYA
0834-   91 06       STA   ($06),Y
0836-   C8          INY
0837-   D0 FB       BNE   $0834
0839-   E6 07       INC   $07
083B-   CA          DEX
083C-   D0 F6       BNE   $0834

; switch to hi-res screen 1 (blank)
083E-   8D 57 C0    STA   $C057
0841-   8D 50 C0    STA   $C050
0844-   8D 54 C0    STA   $C054
0847-   8D 52 C0    STA   $C052

; set up ($3E) vector to point to the
; sector read routine in the disk
; controller ROM
084A-   A9 5C       LDA   #$5C
084C-   85 3E       STA   $3E

; the disk controller ROM always exits
; via $0801, so set that to an RTS so
; we can JSR and not have to set up a
; loop
084E-   A9 60       LDA   #$60
0850-   8D 01 08    STA   $0801

; hmm again
0853-   A9 72       LDA   #$72
0855-   48          PHA

OK, we've now pushed $04/$72 on the
stack. That's probably important.

; multi-sector read
; Y = first logical sector ($01)
; X = last logical sector ($09)
; A = start address high byte ($B7)
0856-   A0 00       LDY   #$00
0858-   84 FC       STY   $FC
085A-   C8          INY
085B-   A9 B7       LDA   #$B7
085D-   A2 09       LDX   #$09

; call multi-sector read routine
085F-   20 65 08    JSR   $0865

; another sector read, this time just
; one sector, into $0400 (X is already
; less than Y on entry, so loop will
; exit after one read)
0862-   A9 04       LDA   #$04
0864-   AA          TAX

; falls through to multi-sector read
; entry point (was also called earlier)
0865-   85 27       STA   $27
0867-   E8          INX
0868-   86 49       STX   $49
086A-   84 F9       STY   $F9

; map logical into physical sector and
; store it in zero page where the disk
; controller ROM will look for it
086C-   B9 83 08    LDA   $0883,Y
086F-   85 3D       STA   $3D

; read sector via disk controller ROM
0871-   20 7E 08    JSR   $087E

; loop until done
0874-   A4 F9       LDY   $F9
0876-   C8          INY
0877-   C4 49       CPY   $49
0879-   90 EF       BCC   $086A
087B-   A5 27       LDA   $27
087D-   60          RTS

; subroutine to read a sector via ($3E)
; which points to $Cx5C, which exits
; via $0801, which is now an "RTS"
; (HOW F---ING ELEGANT IS THAT, RIGHT?)
087E-   A6 2B       LDX   $2B
0880-   6C 3E 00    JMP   ($003E)

; physical to logical sector map
0883- .. .. .. 00 03 05 07 09
0888- 0B 0D 0F 02 04 06 08 0A
0890- 0C 0E 01

That's it. Flexible but compact.

It's a weird combination of reads,
though. It loads a bunch of sectors at
$B700, then the last one at $0400 (!).
That's part of the text page, but it's
hidden during boot because we cleared
the entire hi-res graphics page and
showed that instead.

Of course, we manually pushed $04/$72
on the stack earlier, so once we fall
through to the sector read routine,
read the last sector, and hit the RTS
we put at $0801, we will "return" to
$0472 + 1 = $0473.

Let's interrupt the boot before it gets
there.

                   ~

               Chapter 3
 In Which Things Get Brilliantly Weird


*9600<C600.C6FFM

; set up callback by changing the two
; bytes that are pushed to the stack
96F8-   A9 97       LDA   #$97
96FA-   8D 18 08    STA   $0818
96FD-   A9 04       LDA   #$04
96FF-   8D 54 08    STA   $0854

; start the boot
9702-   4C 01 08    JMP   $0801

; callback is here --
; copy $0400 up to higher memory so it
; survives a reboot
9705-   A0 00       LDY   #$00
9707-   B9 00 04    LDA   $0400,Y
970A-   99 00 24    STA   $2400,Y
970D-   C8          INY
970E-   D0 F7       BNE   $9707

; turn off slot 6 drive motor
9710-   AD E8 C0    LDA   $C0E8

; reboot to my work disk
9713-   4C 00 C5    JMP   $C500

*BSAVE TRACE2,A$9600,L$116
*9600G
...reboots slot 6...
...reboots slot 5...

]BSAVE OBJ.0400-04FF,A$2400,L$100
]CALL -151

The entry point was $0473, so let's
start there. I'll have to leave the
code at $2400. Relative branches will
look correct, but absolute addresses
in $04xx will be +$2000.

*2473L

; zp$4A is important later (see below)
2473-   46 4A       LSR   $4A
2475-   20 B0 04    JSR   $04B0

*24B0L

24B0-   20 33 04    JSR   $0433

*2433L

; call the following line (then fall
; through and do it again)
2433-   20 36 04    JSR   $0436

; save A and Y
2436-   48          PHA
2437-   98          TYA
2438-   48          PHA

; low-level disk stuff (see below)
2439-   A5 FC       LDA   $FC
243B-   85 FD       STA   $FD
243D-   E6 FC       INC   $FC
243F-   A5 FC       LDA   $FC
2441-   29 03       AND   #$03
2443-   0A          ASL
2444-   05 2B       ORA   $2B
2446-   A8          TAY
2447-   B9 81 C0    LDA   $C081,Y

; wait loop
244A-   A9 30       LDA   #$30
244C-   20 A8 FC    JSR   $FCA8

; more low-level disk stuff
244F-   A5 FD       LDA   $FD
2451-   29 03       AND   #$03
2453-   0A          ASL
2454-   05 2B       ORA   $2B
2456-   A8          TAY
2457-   B9 80 C0    LDA   $C080,Y

; more waiting
245A-   A9 30       LDA   #$30
245C-   20 A8 FC    JSR   $FCA8

; restore A and Y on the way out
245F-   68          PLA
2460-   A8          TAY
2461-   68          PLA
2462-   60          RTS

This is a very clever and compact way
to advance the drive head to the next
track. Normally DOS 3.3 keeps track of
this and has a (much more complicated)
routine to move the head back and forth
as needed. But this loader only needs
to move it forward, so the entire
process collapses to this:

1. Set up the Y register to be a slot
   number (x16) plus the appropriate
   phase (0-3, depending on which track
   the drive head is on)

2. LDA $C081,Y to turn on the
   appropriate stepper motor

3. Wait exactly the right amount of
   time (as measured in CPU cycles)

4. LDA $C080,Y to turn off the
   appropriate stepper motor

5. Wait the right amount of time again

...which is exactly what this routine
at $0436 is doing. But that only gets
us halfway there -- literally, it only
moves the drive head by half a track.
But! Since $0433 "falls through" to
$0436, it ends up doing this twice. Two
half tracks equal one whole track, so
calling the routine at $0433 will move
the drive head to the next whole track.

(By the way, this is why it initialized
zero page $FC to $00 at $0858. That's
the "current" track where the drive
head is at boot; it gets updated when
the drive head advances.)

Everything I know about low-level disk
stepping, I learned from this excellent
Usenet post:
macgui.com/usenet/?group=1&id=31160

Continuing from $04B3...

*24B3L

; zero page fiddling (see below)
24B3-   A9 00       LDA   #$00
24B5-   85 41       STA   $41
24B7-   38          SEC
24B8-   66 4A       ROR   $4A

; multi-sector read, similar to the one
; we did in boot0
; Y = first logical sector ($01)
; X = last logical sector ($05)
; A = start address high byte ($08)
24BA-   A9 08       LDA   #$08
24BC-   A0 01       LDY   #$01
24BE-   A2 05       LDX   #$05
24C0-   20 15 04    JSR   $0415

*2415L

; store A in zero page $27, used by the
; disk controller ROM routine as the
; target page to store sectors read
; from disk
2415-   85 27       STA   $27

; X is the final sector to read
2417-   E8          INX
2418-   86 49       STX   $49

; Y is the current sector to read
; (starting with whatever was passed in
; and incrementing until it equals the
; value passed in the X register)
241A-   84 F9       STY   $F9
241C-   98          TYA

; But wait, there's more! Based on the
; high bit of zero page $4A, Y is
; either a logical sector (the map of
; logical->physical sectors is at
; $0463) or a physical sector
241D-   24 4A       BIT   $4A
241F-   30 03       BMI   $2424
2421-   B9 63 04    LDA   $0463,Y

; store physical sector in $3D (again,
; used by the disk controller ROM)
2424-   85 3D       STA   $3D

; read sector by jumping to ($003E),
; which points to $Cx5C (e.g. $C65C if
; booting from slot 6) and exit via
; $0801, which is an RTS by now, so
; this just continues to the next line
2426-   20 00 04    JSR   $0400

; increment sector index
2429-   A4 F9       LDY   $F9
242B-   C8          INY

; are there more sectors to read?
242C-   C4 49       CPY   $49

; yes, branch back and repeat
242E-   90 EA       BCC   $241A

; no, exit with last page (+1) in A
; (disk controller ROM increments this
; after storing sector data, so on exit
; this will be the first page that was
; NOT filled with data in this loop)
2430-   A5 27       LDA   $27
2432-   60          RTS

But wait, it gets even better.

                   ~

               Chapter 4
         Every Byte Is Sacred,
         Every Byte Is Great,
         If A Byte Gets Wasted,
         Woz Gets Quite Irate


Continuing from $04C3...

; move the drive head one phase only,
; to the next HALF track
24C3-   20 36 04    JSR   $0436

[now on track 1.5]

; read more sectors ($06..$0A)
24C6-   A2 0A       LDX   #$0A
24C8-   20 15 04    JSR   $0415

; advance another half track
24CB-   20 36 04    JSR   $0436

[now on track 2]

; read more sectors ($0B..$0F)
24CE-   A2 0F       LDX   #$0F
24D0-   20 15 04    JSR   $0415

; fiddle with $4A again
24D3-   46 4A       LSR   $4A
24D5-   60          RTS

So here's the deal with $4A: we
initialized it at $0473 by a blind LSR,
which clears the high bit. This tells
the multi-sector read routine at $0415
to use logical sectors. Then we set the
high bit at $04B7 with SEC + ROR,
indicating we want $0415 to read
physical sectors. Then we read a few
sectors from track 1, a few from track
1.5, and a few from track 2. Then we
reset $4A with another LSR, and we're
back to using logical sectors.

This explains why my EDD bit copy
failed. This disk is storing data on
half tracks. Worse, it's storing data
on *adjacent* half tracks -- a few from
track 1, a few from track 1.5, and a
few from track 2. Due to limitations of
the Disk II drive mechanism, that would
be virtually impossible for a generic
bit copier to reproduce on a blank
floppy disk.

Every part of this code is brilliant,
AND it fits in a single sector on the
text page, AND it's flexible enough to
read from virtually uncopyable disks.

Continuing from $0478...

*2478L

2478-   A9 40       LDA   #$40
247A-   20 05 04    JSR   $0405

This is very interesting. $0405 looks
like this:

*2405L

2405-   20 0E 04    JSR   $040E
2408-   20 0E 04    JSR   $040E
240B-   20 0E 04    JSR   $040E
240E-   20 33 04    JSR   $0433
2411-   A2 0F       LDX   #$0F
2413-   A0 00       LDY   #$00
2415-   85 27       STA   $27
.
. multi-sector read routine (see above)
.

$0411 sets X and Y to read an entire
track (sector $00 through $0F). Before
that, $040E advances to the next whole
track. And before *that*, we have three
identical JSRs to $040E, each of which
falls through to the next, and
eventually to $040E again.

Thus, calling $040E will advance one
whole track and read one whole track.
Calling $040B will do that twice,
reading each track into consecutive
memory (because the multi-sector read
routine ends with next page in memory
in the accumulator, so you can chain
them and just fill up memory without
having to reset the starting page).

Calling $0408 will do it three times,
and calling $0405 will do it four times
(again, into consecutive memory). So
these two lines of code...

2478-   A9 40       LDA   #$40
247A-   20 05 04    JSR   $0405

...will advance to track $03 and read
tracks $03-$06 into $4000..$7FFF.

Continuing from $047D...

; advance one whole track
247D-   20 33 04    JSR   $0433

[now on track 7]

; read 3 sectors of track 7 into the
; next consecutive address after the
; last read, so $8000..$82FF
2480-   A2 02       LDX   #$02
2482-   20 13 04    JSR   $0413

; read more sectors into $1700..$1FFF
; (still from track 7)
2485-   A9 17       LDA   #$17
2487-   A2 0B       LDX   #$0B
2489-   20 15 04    JSR   $0415

By hook and by crook, we've managed to
fill up $B700..$BFFF (from track 0),
$0800..$2000 (from tracks 1, 1.5, 2,
and 7), and $4000..$82FF (from tracks
3, 4, 5, and 6).

; manually poke in a few values
248C-   A9 EA       LDA   #$EA
248E-   8D 00 08    STA   $0800
2491-   A9 4C       LDA   #$4C
2493-   8D 01 08    STA   $0801

; set up a DOS 3.3-shaped RWTS with
; information about the current track
2496-   A6 2B       LDX   $2B
2498-   8E E9 B7    STX   $B7E9
249B-   20 8E BE    JSR   $BE8E
249E-   A5 FC       LDA   $FC
24A0-   99 78 04    STA   $0478,Y
24A3-   4A          LSR
24A4-   8D 78 04    STA   $0478

; push $B7/$3A to the stack
24A7-   A9 B7       LDA   #$B7
24A9-   48          PHA
24AA-   A9 3A       LDA   #$3A
24AC-   48          PHA

; and exit via HOME routine (wipes this
; code from memory, since we're on the
; text page!)
24AD-   4C 58 FC    JMP   $FC58

Now, I need to be careful about where I
interrupt the boot. I want to capture
the state of memory just before we
start poking individual values into
$0800 and $0801. So I can interrupt the
boot at $048C.

                   ~

               Chapter 5
       In Which We See The Light
       At The End Of The Tunnel,
       And It's A DOS-shaped RWTS
       Which Is A Weird Thing To
       See In A Tunnel, Honestly


*9600<C600.C6FFM

; set up callback #1 and start the boot
96F8-   A9 97       LDA   #$97
96FA-   8D 18 08    STA   $0818
96FD-   A9 04       LDA   #$04
96FF-   8D 54 08    STA   $0854
9702-   4C 01 08    JMP   $0801

; callback #1 is here --
; set up an unconditional break to the
; monitor ($FF59) after we load tracks
; 1, 1.5, 2, 3, 4, 5, 6, and most of 7
; into memory
9705-   A9 4C       LDA   #$4C
9707-   8D 8C 04    STA   $048C
970A-   A9 59       LDA   #$59
970C-   8D 8D 04    STA   $048D
970F-   A9 FF       LDA   #$FF
9711-   8D 8E 04    STA   $048E

; continue the boot
9714-   4C 73 04    JMP   $0473

*BSAVE TRACE3,A$9600,L$117

; fill memory with an unusual byte (to
; more easily spot pages that were
; modified during boot)
*800:FD N 801<800.BEFEM

*BRUN TRACE3
...reboots slot 6...
...read read read...
<beep>

Some manual listing confirms that we've
successfully captured $0800..$1FFF,
$4000..$82FF, and $B700..$BFFF. Hi-res
graphics page 1 is blank (all zeroes,
set at $0829), and the rest of memory
is untouched (still $FD, the unusual
byte I set just before running TRACE3).

$B800+ is a DOS 3.3-shaped RWTS, likely
used to read the rest of the disk after
the custom bootloader zaps itself from
memory by exiting via HOME (which wipes
the text screen).

*C500G
...

]BSAVE OBJ.4000-82FF,A$4000,L$4300
]BRUN TRACE3
...

*2800<800.1FFFM
*C500G
...

]BSAVE OBJ.0800-1FFF,A$2800,L$1800
]BRUN TRACE3

                   ~

               Chapter 6
       Do You Know Demuffin Man,
       Demuffin Man, Demuffin Man?
       Do You Know Demuffin Man
       Who Lives On Floppy Lane?


Using Super Demuffin (with epilogues
$FF $FF $FF), I "copied" the original
disk to a freshly formatted blank
disk. Of course, this only got track
$00 and tracks $08-$22, but that's OK.

                 --v--

     LOCKSMITH 7.0  FAST DISK BACKUP


   R.*******...........................
   W***********************************
HEX 00000000000000001111111111111111222
TRK 0123456789ABCDEF0123456789ABCDEF012
   0.AAAAAAA...........................
   1.AAAAAAA...........................
   2.AAAAAAA...........................
   3.AAAAAAA...........................
   4.AAAAAAA...........................
   5.AAAAAAA...........................
   6.AAAAAAA...........................
   7.AAAAAAA...........................
   8.AAAAAAA...........................
   9.AAAAAAA...........................
   A.AAAAAAA...........................
   B.AAAAAAA...........................
   C.AAAAAAA...........................
   D.AAAAAAA...........................
12 E.AAAAAAA...........................
   F.AAAAAAA...........................
[               ] PRESS [RESET] TO EXIT

                 --^--

Now I get to write everything that I
captured from the corrupted tracks back
to tracks $01-$07.

Here is the original disk layout:

track          | sectors | address
---------------+---------+-------------
1              | $01-$05 | $0800-$0CFF
1.5            | $06-$0A | $0D00-$11FF
2              | $0B-$0F | $1200-$16FF
3              | $00-$0F | $4000-$4FFF
4              | $00-$0F | $5000-$5FFF
5              | $00-$0F | $6000-$6FFF
6              | $00-$0F | $7000-$7FFF
7              | $00-$02 | $8000-$82FF
7              | $03-$0B | $1700-$1FFF

And here is the new layout:

track          | sectors | address
---------------+---------+-------------
1 (was 1/1.5/2)| $00-$0E | $0800-$16FF
1              | $0F     | unused
2              | $00-$0F | unused
3\             | $00-$0F | $4000-$4FFF
4 \            | $00-$0F | $5000-$5FFF
5  } unchanged | $00-$0F | $6000-$6FFF
6 /            | $00-$0F | $7000-$7FFF
7/             | $00-$02 | $8000-$82FF
7              | $03-$0B | $1700-$1FFF

Of course, all the tracks will now have
normal address fields (no more lying
about the track numbers). Also no half
tracks and no spirals. Just, you know,
sectors on a disk. All the spiral track
stuff collapses into a single track, so
track $02 is completely unused.

[S6,D1=demuffin'd copy (T00, T07-T22)]
[S5,D1=my work disk]

]PR#5
...
]CALL -151

*BE00L

; page count (decremented)
BE00-   A9 70       LDA   #$70
BE02-   85 FF       STA   $FF

; logical sector (incremented)
BE04-   A9 00       LDA   #$00
BE06-   85 FE       STA   $FE

; call RWTS to write sector
BE08-   A9 BE       LDA   #$BE
BE0A-   A0 88       LDY   #$88
BE0C-   20 D9 03    JSR   $03D9

; increment logical sector, wrap around
; from $0F to $00 and increment track
BE0F-   E6 FE       INC   $FE
BE11-   A4 FE       LDY   $FE
BE13-   C0 10       CPY   #$10
BE15-   D0 07       BNE   $BE1E
BE17-   A0 00       LDY   #$00
BE19-   84 FE       STY   $FE
BE1B-   EE 8C BE    INC   $BE8C

; Convert to the interleave order that
; this disk expects
BE1E-   B9 40 BE    LDA   $BE40,Y
BE21-   8D 8D BE    STA   $BE8D

; increment page to write
BE24-   EE 91 BE    INC   $BE91

; loop until done with all pages
BE27-   C6 FF       DEC   $FF
BE29-   D0 DD       BNE   $BE08
BE2B-   60          RTS

; sector interleave table
*BE40.BE4F

BE40- 00 06 05 04 03 02 01 0F
BE48- 0E 0D 0C 0B 0A 09 08 07

; RWTS parameter table, pre-initialized
; with slot 6, drive 1, track $01,
; sector $00, address $1000, and RWTS
; write command ($02)
*BE88.BE97

BE88- 01 60 01 00 01 00 FB F7
BE90- 00 10 00 00 02 00 00 60

*BSAVE MAKE,A$BE00,L$98

Looking at the disk map, this will be
easier if I break up one of the chunks
I captured earlier. Part of the file
OBJ.0800-1FFF is loaded from track 1;
the other part from track 7.

*BLOAD OBJ.0800-1FFF,A$800
*BSAVE OBJ.1700-1FFF,A$1700,L$900

OK, here we go.

; clear memory
*800:00 N 801<800.BDFEM

; track 1
*BLOAD OBJ.0800-1FFF,A$1000

; track 2 is unused
*2000:FD N 2001<2000.2FFEM

; tracks 3-7
*BLOAD OBJ.4000-82FF,A$3000
*BLOAD OBJ.1700-1FFF,A$7300

*BE00G
...write write write...

With all this code and data in (mostly)
the same place as the original disk, I
can make just a few patches to the
bootloader:

  1. Modify the routine at $0433 (that
     advances the drive head) so it
     updates zero page $41 with the
     current track.

  2. Modify the routine at $04B0 so it
     reads $0800..$16FF from track 1.
     No tricks, no traps, no spirals.

  3. Patch the second-stage RWTS to
     read standard epilogues.

The sector read routine at $C65C
compares the track listed in the
address field to zero page $41 and
loops forever until it matches. $C600
initializes $41 to 0, and the original
disk never updates $41, but everything
works because the address fields are
corrupted and all claim to be track 0.

The patch at $04B0 (to load track 1
directly into $0800) is made easier by
the fact that we can reuse the entry
point at $040E to read an entire track,
then the other entry point at $0433 to
skip over track 2. This will read one
extra sector into $1700, but it doesn't
matter because we'll overwrite that
later when we read $1700..$1FFF from
track 7.

T00,S0C,$B0 -> A9 08 20 0E 04 4C 33 04

which looks like this (using Disk
Fixer's built-in disassembler):

                 --v--

----------- DISASSEMBLY MODE ----------
00B0:A9 08          LDA   #$08
00B2:20 0E 04       JSR   $040E
00B5:4C 33 04       JMP   $0433

                 --^--

There's no room at $0433, so I'll add a
new routine at $04BB (now unused space)
to increment the track number in zero
page $41.

T00,S0C,$BB -> E6 41 4C 36 04

                 --v--

----------- DISASSEMBLY MODE ----------
00BB:E6 41          INC   $41
00BD:4C 36 04       JMP   $0436

                 --^--

Now $0433 can call $04BB to increment
the track number in zero page before
advancing the drive arm.

T00,S07,$34: 36 -> BB

                 --v--

----------- DISASSEMBLY MODE ----------
0033:20 BB 04       JSR   $04BB

                 --^--

Finally, a few patches to the RWTS that
reads the rest of the disk after the
bootloader exits. The only difference
was the epilogues; it expects $FF $FF
instead of the standard $DE $AA.

T00,S04,$35: FF -> DE
T00,S04,$3F: FF -> AA
T00,S04,$91: FF -> DE
T00,S04,$9B: FF -> AA

(The write routines in this RWTS still
use the standard $DE $AA, which leads
me to believe that the original disk
never writes to itself -- all reads of
rewritten sectors would fail due to
mismatched epilogues!)

And that should do it.

]PR#6
...works, and it is glorious...

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1190
------------------EOF------------------
